{-# OPTIONS_GHC -Wall -Werror #-}
{-# LANGUAGE TypeFamilies #-}

module Size where

import Math
import Projectable
import Vector2D
import Data.VectorSpace hiding (Scalar)
import qualified Data.VectorSpace as VS
import Control.Parallel.Strategies

-- TODO: Isn't a size just a vector2???
newtype Size = SZ { unSZ :: Vector2 } deriving Eq

instance Show Size where
  show (SZ (V2 x y)) = "("++showN x++" * "++showN y++")"


inSZ :: (Vector2 -> Vector2) -> Size -> Size
inSZ f = SZ . f . unSZ

inSZ2 :: (Vector2 -> Vector2 ->Vector2) -> Size -> Size -> Size
inSZ2 f a b = SZ $ f (unSZ a) (unSZ b)

-- | Construct a size
size :: Scalar -> Scalar -> Size
size x y = SZ (vector2 x y)
{-# INLINE size #-} 

instance Project2 Size where
  project2 = project2 . unSZ
  compose2 = SZ . compose2

instance AdditiveGroup Size where
  zeroV = SZ zeroV
  (^+^) = inSZ2 (^+^)
  negateV = inSZ negateV

instance VectorSpace Size where
  type VS.Scalar Size = Scalar
  (*^) s = inSZ (s *^) 

instance InnerSpace Size where
  a <.> b = unSZ a <.> unSZ b

instance NFData (Size)

class IsSize a where
  getSize :: a -> Size
  getWidth :: a -> Scalar
  getHeight :: a -> Scalar
  
  withSize :: Size -> a -> a
  withWidth :: Scalar -> a -> a
  withHeight :: Scalar  -> a -> a
  
  mapSize :: (Size -> Size) -> a -> a
  mapWidth :: (Scalar -> Scalar) -> a -> a
  mapHeight :: (Scalar -> Scalar) -> a -> a
  {-# INLINE getWidth #-} 
  {-# INLINE getHeight #-} 
  {-# INLINE withWidth #-} 
  {-# INLINE withHeight #-} 
  {-# INLINE mapWidth #-} 
  {-# INLINE mapHeight #-} 

  withSize = mapSize . const
  withWidth = mapWidth . const
  withHeight = mapHeight . const

instance IsSize Size where
  getSize = id
  getWidth  = getX . unSZ
  getHeight = getY . unSZ
  
  mapSize f = f
  mapWidth f = inSZ (mapX f)
  mapHeight f = inSZ (mapY f)

